package hasthi.actuators;

import hasthi.actuators.actions.ActionCallback;
import hasthi.common.DistributedLogger;
import hasthi.common.HasthiError;
import hasthi.common.HasthiException;
import hasthi.common.constants.HasthiConstants;
import hasthi.decision.planning.Planner;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/** 
 * Every action must extends this class, this is not a interface as there must be some 
 * work management action must do before and after running the action. If required we can 
 * provide a interface which will be wrapped by a subclass of management action. 
 * @author hperera
 *
 */

public abstract class ManagementAction implements Runnable{
    
    protected ActionContext actionContext;
    protected static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    protected ActionCallback callback;
    private long startedTime;
    private Throwable errors = null; 
    /**
     * This method will be called by the system to before execute is called to
     * hand over a reference to underline system 
     * @param actionContext
     */
    public void init(ActionContext actionContext) {
        this.actionContext = actionContext;
        this.startedTime = System.currentTimeMillis();
        if(actionContext.getPlanner() != null){
            actionContext.getPlanner().actionSceduled(this);
        }
    }
    
    
    public boolean isCompleted() {
        return true;
    }

    public PreCondition getPreconditions() {
        return null;
    }

    public int getPriorty() {
        return 0;
    }
    
    /**
     * this is a ID that uniquely identify the Action
     */
    public abstract String getActionID();
    protected abstract void execute()throws HasthiException;
    
    

    public ActionCallback getCallback() {
        return callback;
    }


    public void setCallback(ActionCallback callback) {
        this.callback = callback;
    }
    
    public ActionContext getActionContext() {
        return actionContext;
    }

    public final void  run() {
        if(actionContext == null){
            throw new HasthiError("Action context must be initialized by this point, if you see this it is a error at Hashti");
        }
        Planner planner = actionContext.getPlanner();
        DistributedLogger dlogger = actionContext.getBaseContext().getDlogger();
        try {
            planner.actionActive(this);
            long start = System.nanoTime();
            execute();
            planner.actionCompleted(this);
            
            dlogger.info(new StringBuffer("Action ").append((actionContext.isCoordinator()?"Coordinator:":"Manager:"))
                    .append(getActionID()).append(" Completed in (").append((System.nanoTime() - start)/1000000).append(") ms").toString());
            if(callback != null){
                callback.actionSucessful(this);
            }
        } catch (Throwable e) {
            errors = e;
            dlogger.info("Action " + (actionContext.isCoordinator()?"Coordinator:":"Manager:")+getActionID() + " Failed");
            planner.actionFailed(this, e);
            if(callback != null){
                callback.actionFailed(this,e);
            }
        }
    }


    @Override
    public String toString() {
        return getActionID();
    }

    public long getStartedTime() {
        return startedTime;
    }


    public Throwable getErrors() {
        return errors;
    }
    
}
